using System;
using System.Runtime.InteropServices;

namespace PDUConverter
{
    /// <summary>
    /// TP-ST / TP-Status.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct MessageStatus
    {
        private byte status;
        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.MessageStatus" />.
        /// </summary>
        /// <param name="status">The status code.</param>
        public MessageStatus(byte status)
        {
            this.status = status;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.MessageStatus" />.
        /// </summary>
        /// <param name="status">One of the <see cref="T:GsmComm.PduConverter.KnownMessageStatus" /> values.</param>
        public MessageStatus(KnownMessageStatus status)
        {
            this.status = (byte) status;
        }

        /// <summary>
        /// Gets the status category.
        /// </summary>
        /// <remarks>
        /// If the valus does not fall into one of the predefined categories, <see cref="F:GsmComm.PduConverter.StatusCategory.Reserved" />
        /// is returned.
        /// </remarks>
        public StatusCategory Category
        {
            get
            {
                if ((this.status >= 0) && (this.status <= 0x1f))
                {
                    return StatusCategory.Success;
                }
                if ((this.status >= 0x20) && (this.status <= 0x2f))
                {
                    return StatusCategory.TemporaryErrorWithRetry;
                }
                if ((this.status >= 0x40) && (this.status <= 0x5f))
                {
                    return StatusCategory.PermanentError;
                }
                if ((this.status >= 0x60) && (this.status <= 0x7f))
                {
                    return StatusCategory.TemporaryErrorNoRetry;
                }
                return StatusCategory.Reserved;
            }
        }
        /// <summary>
        /// Checks if the message status exists within the known status list.
        /// </summary>
        /// <returns>true if the status is a known status, otherwise false.</returns>
        public bool IsKnownStatus()
        {
            return Enum.IsDefined(typeof(KnownMessageStatus), this.status);
        }

        /// <summary>
        /// Retrieves the known status of the current message status.
        /// </summary>
        /// <returns>Ae <see cref="T:GsmComm.PduConverter.KnownMessageStatus" /> representing the message status.</returns>
        /// <remarks>Check first with <see cref="M:GsmComm.PduConverter.MessageStatus.IsKnownStatus" /> before calling this method.</remarks>
        /// <exception cref="T:System.ArgumentException">Message status is not a known message status.</exception>
        public KnownMessageStatus GetKnownStatus()
        {
            if (!this.IsKnownStatus())
            {
                throw new ArgumentException(this.status.ToString() + " is not a known message status.");
            }
            return (KnownMessageStatus) Enum.Parse(typeof(KnownMessageStatus), this.status.ToString());
        }

        /// <summary>
        /// Returns the string representation of the status.
        /// </summary>
        /// <returns>The string representation of the known status if it is a known status,
        /// the numerical status value otherwise.</returns>
        public override string ToString()
        {
            if (this.IsKnownStatus())
            {
                return this.GetKnownStatus().ToString();
            }
            return this.status.ToString();
        }

        /// <summary>
        /// Returns the byte representation of the status.
        /// </summary>
        /// <returns>A <see cref="T:System.Byte" /> value representing the object's value.</returns>
        public byte ToByte()
        {
            return this.status;
        }

        /// <summary>
        /// Converts the value of the specified <see cref="T:GsmComm.PduConverter.MessageStatus" /> instance into a <see cref="T:System.Byte" />.
        /// </summary>
        /// <param name="s">A <see cref="T:GsmComm.PduConverter.MessageStatus" /> object.</param>
        /// <returns>A new <see cref="T:System.Byte" /> value representing the object's value.</returns>
        public static implicit operator byte(MessageStatus s)
        {
            return s.ToByte();
        }

        /// <summary>
        /// Converts the specified <see cref="T:System.Byte" /> value into a <see cref="T:GsmComm.PduConverter.MessageStatus" /> object.
        /// </summary>
        /// <param name="b">A <see cref="T:System.Byte" /> value.</param>
        /// <returns>A new <see cref="T:GsmComm.PduConverter.MessageStatus" /> object whose value equals the value of the
        /// <b>b</b> parameter.</returns>
        public static implicit operator MessageStatus(byte b)
        {
            return new MessageStatus(b);
        }

        /// <summary>
        /// Converts the specified <see cref="T:GsmComm.PduConverter.KnownMessageStatus" /> into a <see cref="T:GsmComm.PduConverter.MessageStatus" />.
        /// </summary>
        /// <param name="s">A <see cref="T:GsmComm.PduConverter.KnownMessageStatus" /> value.</param>
        /// <returns>A new <see cref="T:GsmComm.PduConverter.MessageStatus" /> object whose message status represents the value of the <b>s</b> parameter.</returns>
        public static implicit operator MessageStatus(KnownMessageStatus s)
        {
            return new MessageStatus(s);
        }
    }
}